package com.lion.httpapi;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.lion.httpapi.annotation.HttpPath;
import com.lion.httpapi.config.Configuration;
import com.lion.httpapi.exception.ConfigurationException;
import com.lion.httpapi.exception.DuplicateBindException;
import com.lion.httpapi.exception.PathBindException;
import com.lion.httpapi.handler.HttpCorsServerInitializer;
import com.lion.httpapi.servlet.ActionMethod;
import com.lion.httpapi.servlet.ApiRequest;
import com.lion.httpapi.servlet.ApiResponse;
import com.lion.httpapi.tools.AnnotationTools;
import com.lion.httpapi.tools.ScanPackage;
import com.lion.httpapi.tools.StringTools;

/**
 * 创建NettyHttp服务
 * 
 * @author liyongyao
 *
 */
public class HttpServer {
	private static final boolean SSL = System.getProperty("ssl") != null;
	private static final Map<String, Method> bindMap = new HashMap<String, Method>();
	private static final Map<String, Object> singletonSpace = new HashMap<String, Object>();
	private static Configuration config = Configuration.getInstance();
	private static Log log = LogFactory.getLog(HttpServer.class);

	public void start() throws Exception {
		final SslContext sslCtx;
		if (SSL) {
			SelfSignedCertificate ssc = new SelfSignedCertificate();
			sslCtx = SslContextBuilder.forServer(ssc.certificate(),
					ssc.privateKey()).build();
		} else {
			sslCtx = null;
		}
		bindInterface();
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.handler(new LoggingHandler(LogLevel.DEBUG))
					.childHandler(new HttpCorsServerInitializer(sslCtx));
			int serverPort = config.getInt("server.netty.port");
			if (serverPort == -1 || serverPort > 65535) {
				throw new ConfigurationException(
						"server.netty.port 必须设置（0到65535的整数）");
			}
			b.bind(Integer.valueOf(serverPort)).sync().channel().closeFuture()
					.sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	/**
	 * 绑定接口
	 * 
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public void bindInterface() throws InstantiationException,
			IllegalAccessException {
		String packagePath = config.get("server.intf.bind");
		if (StringTools.isNull(packagePath)) {
			System.out.println("启动失败，server.intf.bind未定义");
			System.exit(-1);
			return;
		}
		Set<Class<?>> classes = ScanPackage.getClasses(packagePath);
		for (Class<?> clazz : classes) {
			if (readAnnotation(clazz)) {
				Object o = clazz.newInstance();
				singletonSpace.put(clazz.getName(), o);
			}
		}
	}

	/**
	 * 读取class的注解
	 * 
	 * @param clazz
	 */
	@SuppressWarnings("unchecked")
	private boolean readAnnotation(Class<?> clazz) {
		boolean bindFlag = false;
		HttpPath baseAnn = (HttpPath) AnnotationTools
				.findClassAnnotation(clazz, HttpPath.class);
		String basePath = "";
		if (baseAnn != null) {
			basePath = baseAnn.value();
		}
		Map<Method, HttpPath> methods = (Map<Method, HttpPath>) AnnotationTools
				.findMethodAnnotation(clazz, HttpPath.class);
		StringBuilder path = new StringBuilder();
		for (Entry<Method, HttpPath> method : methods.entrySet()) {
			if (method.getKey().getParameterTypes().length != 2
					|| method.getKey().getParameterTypes()[0] != ApiRequest.class
					|| method.getKey().getParameterTypes()[1] != ApiResponse.class) {
				throw new PathBindException("方法：" + method.getKey()
						+ "的定义不规范，参数必须为：NettyRequest,NettyResponse");
			}
			ActionMethod[] actions = method.getValue().method();
			for (ActionMethod action : actions) {
				path.append(action.name() + ":");
				path.append(basePath);
				String methodName = method.getValue().value();
				if (methodName.trim().equals("")) {
					methodName = method.getKey().getName();
				}
				path.append(methodName);
				if (bindMap.containsKey(path.toString())) {
					throw new DuplicateBindException("路径[ " + path.toString()+ "]已被绑定，无法重复绑定");
				}
				bindMap.put(path.toString(), method.getKey());
				path.delete(0, path.length());
				if (!bindFlag) {
					bindFlag = true;
				}
			}
		}
		return bindFlag;
	}

	public static void main(String[] args) throws Exception {
		HttpServer server = new HttpServer();
		log.info("Http Server listening on " + config.get("server.netty.port")
				+ " ...");
		server.start();
	}

	public static Method findMethodByPath(String path) {
		return bindMap.get(path);
	}

	public static Object findObjectByClassName(String className) {
		return singletonSpace.get(className);
	}
}